package com.tmt.datatable.editor;

import com.tmt.datatable.model.*;
import com.tmt.helper.PictureRotateHelper;
import com.tmt.helper.StringHelper;

import java.io.Serializable;
import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Editor model, 验证时需要指定配套使用 @Validated @EditorValid 注解
 * Created by Stark on 2017/6/4.
 */
public class EditorModel<T> implements Serializable {

    /**
     * 文件上传
     */
    public static final String ACTION_UPLOAD = "upload";
    /**
     * 创建操作
     */
    public static final String ACTION_CREATE = "create";
    /**
     * 修改操作
     */
    public static final String ACTION_EDIT = "edit";
    /**
     * 删除操作
     */
    public static final String ACTION_REMOVE = "remove";

    /**
     * action 关键字
     */
    private static final String KEY_ACTION = "action";
    private static final String KEY_UPLOAD_FIELD = "uploadField";

    /**
     * 查找所有的方括号包裹的属性
     */
    private static final Pattern pattern = Pattern.compile("\\[(.*?)\\]");
    private static final Pattern indexPattern = Pattern.compile("^\\[\\d+\\]$");

    /**
     * 执行的操作
     */
    private transient String action;
    /**
     * 上传的文件域
     */
    private transient String uploadField;

    /**
     * 服务器返回的错误信息
     */
    private String error;

    /**
     * 数据域, &lt;idSrc, data&gt;
     */
    private List<T> data;

    /**
     * 数据域错误信息
     */
    private List<FieldError> fieldErrors = null;

    /**
     * 上传文件的信息
     */
    private FileWrapper files = new FileWrapper();

    /**
     * 文件上传的信息
     */
    private Upload upload = new Upload("0");

    public static Pattern getPattern() {
        return pattern;
    }

    public static Pattern getIndexPattern() {
        return indexPattern;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getUploadField() {
        return uploadField;
    }

    public void setUploadField(String uploadField) {
        this.uploadField = uploadField;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

    public List<FieldError> getFieldErrors() {
        return fieldErrors;
    }

    public void setFieldErrors(List<FieldError> fieldErrors) {
        this.fieldErrors = fieldErrors;
    }

    public FileWrapper getFiles() {
        return files;
    }

    public void setFiles(FileWrapper files) {
        this.files = files;
    }

    public Upload getUpload() {
        return upload;
    }

    public void setUpload(Upload upload) {
        this.upload = upload;
    }

    public EditorModel() {
        data = new ArrayList<>();
    }

    public static <T> EditorModel<T> from(Class<T> clazz, Map<String, String[]> map,
                                          IFileDataHolder holder,
                                          IFieldValueSetter fieldValueSetter,
                                          IUploadActionHandler uploadHandler) {
        EditorModel<T> model = new EditorModel<>();

        String currentAction = map.get(KEY_ACTION)[0];
        model.setAction(currentAction);
        if (ACTION_EDIT.equals(currentAction) || ACTION_CREATE.equals(currentAction) || ACTION_REMOVE.equals(currentAction)) {
            model.handleData(clazz, map, currentAction, fieldValueSetter);
        } else if (Objects.equals(currentAction, ACTION_UPLOAD)) {
            try {
                model.setUploadField(map.get(KEY_UPLOAD_FIELD)[0]);
                holder.validate(clazz, model.uploadField);
                Map<String, FileInfo> fileInfoMap = model.handleUpload(clazz, holder, fieldValueSetter, uploadHandler, ACTION_UPLOAD);
                if (fileInfoMap.size() > 0) {
                    model.setFiles(new FileWrapper(fileInfoMap));
                    FileInfo fi = (FileInfo) fileInfoMap.values().toArray()[0];
                    model.setUpload(new Upload(fi.getId()));
                }
            } catch (Exception ex) {
                FieldError error = new FieldError(model.getUploadField(), ex.getMessage());
                model.setFieldErrors(Arrays.asList(error));
                model.setError(MessageFormat.format("上传文件(属性={0})出现异常:{1}", model.getUploadField(), ex.getMessage()));
            }
        }
        return model;
    }

    /**
     * 返回空结果，删除等情况
     */
    public static <T> EditorModel<T> empty() {
        return new EditorModel<>();
    }

    private List<String> getIdList(Stream<String> keys) {
        return keys.map(c -> {
            Matcher matcher = pattern.matcher(c);
            return matcher.find() ? matcher.group(0) : "";
        }).distinct().filter(c -> !c.isEmpty()).collect(Collectors.toList());
    }

    /**
     * 把 data[id][user][name] 类似的键改为  user.name
     */
    private String toModelKey(String jsonKey) {
        List<String> groupNames = new LinkedList<>();
        Matcher matcher = pattern.matcher(jsonKey);
        while (matcher.find()) {
            groupNames.add(matcher.group());
        }

        if (groupNames.size() > 1) {
            StringBuilder sb = new StringBuilder();
            String tempName;
            String appended;
            for (int i = 1, j = groupNames.size(); i < j; i++) {
                tempName = groupNames.get(i);
                Matcher indexMatcher = indexPattern.matcher(tempName);
                if (indexMatcher.find()) {
                    appended = tempName;
                } else {
                    tempName = tempName.replace("[", "").replace("]", "");
                    if (sb.length() == 0) {
                        appended = tempName;
                    } else {
                        appended = "." + tempName;
                    }
                }

                sb.append(appended);
            }

            return sb.toString();
        } else {
            return "";
        }
    }

    /**
     * 处理数据
     *
     * @param clazz  当前类
     * @param params 当前类参数
     */
    private void handleData(Class<T> clazz, Map<String, String[]> params, String action, IFieldValueSetter fieldValueSetter) {
        Map<String, String[]> map = params.entrySet().stream()
                .filter(c -> c.getKey().startsWith("data["))
                .sorted(new KeySorter())
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

        List<String> idList = getIdList(map.keySet().stream());
        if (idList != null && idList.size() > 0) {
            for (String id : idList) {
                T entity = null;
                try {
                    entity = clazz.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }

                if (entity == null) {
                    continue;
                }

                String dataPrefix = MessageFormat.format("data{0}", id);
                LinkedHashMap<String, String[]> dataMap = new LinkedHashMap<>();
                map.entrySet().stream()
                        .filter(c -> c.getKey().startsWith(dataPrefix))
                        .sorted(new KeySorter())
                        .forEachOrdered(c -> dataMap.put(toModelKey(c.getKey()), c.getValue()));

                for (Map.Entry<String, String[]> next : dataMap.entrySet()) {
                    try {
                        fieldValueSetter.setFieldValue(entity, next.getKey(), next.getValue().length == 1 ? next.getValue()[0] : next.getValue(), action);
                    } catch (Exception ignore) {
                    }
                }

                data.add(entity);
            }
        }
    }

    private <T> Map<String, FileInfo> handleUpload(Class<T> clazz,
                                                   IFileDataHolder holder,
                                                   IFieldValueSetter fieldValueSetter,
                                                   IUploadActionHandler uploadHandler, String action) throws Exception {
        Map<String, FileInfo> mapResult = new HashMap<>();
        if (uploadHandler == null)
            return mapResult;

        String propertyName = holder.getFieldName();
        if (fieldValueSetter != null)
            propertyName = fieldValueSetter.getFieldMappingHandlerProxy().getValidFieldName(clazz, propertyName, null, action);

        //保存文件。
        UploadedFile uploadedFile = uploadHandler.saveFile(clazz, propertyName, holder);

        //图片旋转检测和处理。
        PictureRotateHelper.tryRotatePicture(uploadedFile.getFilePth());

        String[] fileNames = StringHelper.splitWithoutEmpty(uploadedFile.getFilePth(), "/");
        String saveFileName = !StringHelper.isEmpty(uploadedFile.getId()) ? uploadedFile.getId() : fileNames[fileNames.length - 1];

        FileInfo fileInfo = new FileInfo();
        fileInfo.setId(saveFileName);
        fileInfo.setFileName(holder.getFileName());
        fileInfo.setFileSize(holder.getFileLength());
        fileInfo.setSystemPath(uploadedFile.getFilePth());
        fileInfo.setWebPath(uploadedFile.getFileUrl());

        mapResult.put(fileInfo.getId(), fileInfo);

        return mapResult;
    }

}
